JPA Tutorial 3 - FAQ
  • Phone number length is set to 11 in the annotation in the exercise, but phone numbers are 12. What's happening? It will get truncated (in hypersonic)
  • TDD: Is there a tool (eclipse plugin) to generate a concrete class based on a test case? Use Quick-fix (Ctrl-1 or click the icon on the sidebar) to create the class, and stub methods.
  • When do Named Queries get compiled? When you create the EntityManager factory.
  • Is there something that can use to see the db structure based on the mappings? After the entitymanager creates the db, use something like DB Visualizer

What did we learn/observe so far (mid-day)?

  • varargs for methods
  • cascading types - persist, all, refresh. propagates operations (merge/update/persist/refresh) across entities. Saves you from having to persist things.
  • jpa requires jdk 1.5
  • the @column annotation sets the name of the column in the database. also lets you specify things like width/length (attribute of the annotation).
  • debugging good
  • eclipse can gen hashcode/equals

What did we learn/observe so far (end of day)?

  • Brett: Why we use a DAO Encapsulates data access, hides entity manager calls. Brett: Seperation of Concerns.
  • Brett: Contrast the Library to the DAO's Library is not an entity. Library is the 'go-between' (brett: facade design pattern).
  • Brett: Set em with a setter....why? When you get your objects, they're fully configured (nice). Brett: makes it more testable
  • Brett: What's @column for? Defines attributes for a particular field in DB. Brett: affects physical characteristics as opposed to logical.
  • Brett: What do you think about variables args? saves lines of code
  • Brett: what is suppresswarnings for? brett: hide a warning based on actual query parameter type (not available at compile-time)
  • Brett: named query and named queries?, why not put multiple named queries on the same object? You can have only one version of the annotation on a given object
  • Brett: opinions on base dao? confusing, brett: heavy-handed/overkill
  • Brett: EntityManagerBaseTest vs. BaseDAODBTest? helps if you decide you want to change how you're accessing your database.
  • Brett: opinions on refactoring as you go? have to look at code to understand what's changing (head nods)
  • Brett: overriding methods? (annotation) gives compiler ability to flag problem. some class discussion of when needed, when not...
  • Brett: @temporal Brett:time/datastamp

What else have you learned today?
  • Specifying the field for the join table
  • Comments in the code today were more helpful (than yesterday)
  • Cascading with a many-to-many relationship -- you can only put mappedBy on one side, but you can put cascade attributes on both sides. Cascading is for convenience -- less calls to entity manager.
  • In the book where define many-to-one (loan), there's an orderBy? defines the sort order

Day 3 - Tutorial #3
Questions

  • Patron has a one-to-many for fines, but no many-to-one on the fines side. Why? Just an example of a uni-directional relationship.
  • Why is insertable false? (in exercise) We think: So that loan has to point at things which already exist. (as opposed to creating new loan/patron at same time)
  • Seems like more stuff should be done in the library ? Hold off on that because the next few exercises build up additional requirements which may change your mind

What else have you learned today?
  • How we can create/persist a given entity
  • Named queries
  • Query syntax is better/easier

Notes for Brett:
  • Need to break up tutorial a bit more.


Responses to questions in the exercises

  • A Patron can return a book while having an overdue book
Below are the tests to check everything:
And the new feature that doesn't allow to checkout with overdue books.

These tests are in LibraryTest.java
    @Test
    public void patronHasNumberOfOverdueBooks() {
        final Patron p = createPatron();
        final Book b1 = createBook();
        final Book b2 = createBook();
        library.checkout(p.getId(), CURRENT_DATE, b1.getId());
        int numOfBooks;
        numOfBooks = library.getNumberOfOverdueBooksOfPatron(p, CURRENT_PLUS_14);
        assertEquals(0, numOfBooks);
        library.checkout(p.getId(), CURRENT_PLUS_8, b2.getId());
        numOfBooks = library.getNumberOfOverdueBooksOfPatron(p, CURRENT_PLUS_15);
        assertEquals(1, numOfBooks);
        library.returnBook(CURRENT_PLUS_8, b1.getId());
        numOfBooks = library.getNumberOfOverdueBooksOfPatron(p, CURRENT_PLUS_15);
        assertEquals(0, numOfBooks);
    }
 
    // My change
    @Test
    public void patronCannotCheckoutWithOverdueBooks() {
        final Patron p = createPatron();
        final Book b1 = createBook();
        library.checkout(p.getId(), CURRENT_DATE, b1.getId());
 
        final Book b2 = createBook();
        try {
            library.checkout(p.getId(), CURRENT_PLUS_15, b2.getId());
            fail(String.format("1. Should have thrown exception: %s", PatronHasOverdueBooks.class
                    .getName()));
        }
        catch (PatronHasOverdueBooks e) {
            assertEquals(1, e.getNumberOfOverdueBooks());
            final Book b3 = createBook();
            library.checkout(p.getId(), CURRENT_DATE, b2.getId());
            try {
                library.checkout(p.getId(), CURRENT_PLUS_15, b3.getId());
                fail(String.format("2. Should have thrown exception: %s",
                        PatronHasOverdueBooks.class.getName()));
            }
            catch (PatronHasOverdueBooks e1) {
                assertEquals(2, e1.getNumberOfOverdueBooks());
            }
            int numOfBooks = library.getNumberOfOverdueBooksOfPatron(p, CURRENT_PLUS_14);
            assertEquals(0, numOfBooks);
        }
    }
 

And here are the changes in project:

There is a new exception class called: PatronHasOverdueBooks
Here it is:
package exception;
 
/**
 * Thrown when a Patron attempts checkout while he has overdue books.
 */
public class PatronHasOverdueBooks extends RuntimeException {
    private static final long serialVersionUID = 7328551028997485470L;
    private final int numberOfOverdueBooks;
    public PatronHasOverdueBooks(final int numberOfOverdueBooks) {
        this.numberOfOverdueBooks = numberOfOverdueBooks;
    }
    public int getNumberOfOverdueBooks() {
        return this.numberOfOverdueBooks;
    }
 
}
 

In Library class, I added to the checkout method a validation for overdue books:
    public void checkout(final Long patronId, final Date checkoutDate, final Long... bookIds) {
        ...
        // Checking number of overdue books of the Patron
        int booksOverdue = getNumberOfOverdueBooksOfPatron(p, checkoutDate);
        if (booksOverdue != 0) {
            throw new PatronHasOverdueBooks(booksOverdue);
        }
 
        ...
    }
 

Also, in Library class, the new method: getNumberOfOverdueBooksOfPatron
    public int getNumberOfOverdueBooksOfPatron(final Patron patron, final Date compareDate) {
        return getLoanDao().numberOfOverdueBookOfPatron(patron, compareDate);
    }
 

LoanDao can return the number of overdue books of specific Patron:
    /**
     * Return number of overdue books of this patron
     *
     * @param patron
     * @param compareDate
     * @return int - The number of overdue books
     */
    public int numberOfOverdueBookOfPatron(final Patron patron, final Date compareDate) {
        final Query query = getEm().createNamedQuery("Loan.overdueBooksOfPatron");
        query.setParameter("date", compareDate);
        query.setParameter("patron", patron);
 
        return query.getResultList().size();
 
    }
 


It uses a new named-query. One of the parameter is the Patron (not the patron's id). I wanted to see how the join works ...
@NamedQuery(name = "Loan.overdueBooksOfPatron", query = "SELECT l.book FROM Loan l WHERE l.dueDate < :date AND l.patron = :patron")